package com.lagou.edu.filter;

import com.lagou.edu.bean.RequestCount;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import org.springframework.http.HttpCookie;

import java.util.*;
import java.util.concurrent.*;

/**
 * 定义全局过滤器，会对所有路由生效
 * @author zjy
 */
@Slf4j
@Component
public class MyFilter implements GlobalFilter, Ordered {


    @Value("${ip.rejectCount}")
    private Integer rejectCount;

    private static Integer REJECT_TIME;

    /**
     * 解决不能给静态变量赋值的问题
     * @param rejectTime
     */
    @Value("${ip.rejectTime}")
    public void setRejectTime(Integer rejectTime){
        REJECT_TIME = rejectTime;
    }

    private static String SESSION_ID = "lagouSessionId";

    private static String REGISTER_URL = "/user/register";

    private static String USER_INFO = "/user/info";


    private static ConcurrentHashMap<String,List<RequestCount>> ipCountMap = new ConcurrentHashMap<>();
//    private CopyOnWriteArrayList<ConcurrentHashMap<String,RequestCount>> ipCountList = new CopyOnWriteArrayList<>();

    static {
        //定时清除过期访问的ip信息
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        executor.scheduleWithFixedDelay(
                new DoJudge(),
                0,
                5,
                TimeUnit.SECONDS);
    }

    /**
     * 过滤器核心方法
     * @param exchange 封装了request和response对象的上下文
     * @param chain 网关过滤器链（包含全局过滤器和单路由过滤器）
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 思路：获取客户端ip，判断是否在黑名单中，在的话就拒绝访问，不在的话就放行
        // 从上下文中取出request和response对象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        MultiValueMap<String, HttpCookie> cookieMultiValueMap =  request.getCookies();

        HttpCookie cookies = cookieMultiValueMap.getFirst(SESSION_ID);
        if(cookies != null && !Objects.equals(cookies.getValue(),"0")){
            log.info("cookie name：{},cookie value ：{}",cookies.getName(),cookies.getValue());
        }else{
            String path = request.getURI().getPath();
            //暂时只有获取用户信息需要过滤
            if(path.contains(USER_INFO)){
                response.setStatusCode(HttpStatus.FORBIDDEN);
                String msg = "未获取到用户信息";
                DataBuffer wrap = response.bufferFactory().wrap(msg.getBytes());
                return response.writeWith(Mono.just(wrap));
            }
        }
        Mono<Void> rejectVoidMono = doRejectJudge(request, response, exchange, chain);
        if(rejectVoidMono != null){
            return rejectVoidMono;
        }
        // 合法请求，放行，执行后续的过滤器
        return chain.filter(exchange);
    }



    public Mono<Void> doRejectJudge(ServerHttpRequest request,ServerHttpResponse response,ServerWebExchange exchange, GatewayFilterChain chain){
        // 从request对象中获取客户端ip
        String clientIp = request.getRemoteAddress().getHostString();
        System.out.println("ip >>>> " + clientIp);
        List<RequestCount> requestCounts = ipCountMap.get(clientIp);

        String path = request.getURI().getPath();
        //注册接口进行IP防爆刷控制
        if(path.contains(REGISTER_URL)){
            //限制访问频率
            if(!CollectionUtils.isEmpty(requestCounts)){
                if(requestCounts.size() >= rejectCount){
                    response.setStatusCode(HttpStatus.SEE_OTHER);
                    log.error("=====>IP:" + clientIp + " 您频繁进⾏注册，请求已被拒绝！");
                    String msg = "您频繁进行注册，请求已被拒绝！请" + REJECT_TIME + "分钟后再试！";
                    DataBuffer wrap = response.bufferFactory().wrap(msg.getBytes());
                    return response.writeWith(Mono.just(wrap));
                }
            }
            List<RequestCount> requestCountList = ipCountMap.get(clientIp);
            if(CollectionUtils.isEmpty(requestCountList)){
                requestCountList = new CopyOnWriteArrayList<>();
            }
            RequestCount requestCount = new RequestCount(path,System.currentTimeMillis());
            requestCountList.add(requestCount);
            ipCountMap.put(clientIp,requestCountList);
        }
        return null;
    }


    /**
     * 返回值表示当前过滤器的顺序(优先级)，数值越小，优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }



    private static class DoJudge implements Runnable{
        @Override
        public void run() {
            try {
                log.info(">>>>>>> 执行定时清理请求的IP信息 >>>>>>> " + ipCountMap);
                if(!ipCountMap.isEmpty()){
                    for(Map.Entry<String, List<RequestCount>> entry : ipCountMap.entrySet()){
                        List<RequestCount> countList = entry.getValue();
                        if(!CollectionUtils.isEmpty(countList)){
                            for (RequestCount requestCount : countList) {
                                if(System.currentTimeMillis() - requestCount.getExpire() > REJECT_TIME*1000*60){
                                    countList.remove(requestCount);
                                }
                            }
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}
